/**
* 一、为什么要关心类型大小？
* 1.rust需要知道类型的某些细节，例如大小。编译器需要在编译时知道类型的大小，
*  以便进行内存分配和释放的静态检查。这有助于确保内存操作的正确性和安全性
* 2.Rust编译器利用类型大小的信息进行各种编译时优化，如内联、循环展开和指令选择等。
* 这些优化有助于提高代码的运行时性能，确保Rust程序的高效性
* 总之，rust作为一个静态类型语言，必须在编译的时候知道类型的大小，以便进行有效的内存管理和优化。

*
* 二、动态大小类型（DST）

* 不得不存在类型-不固定大小的类型  - 动态大小类型（dynamically sized types）
* 有时候也称为DST或unsized types
*
* 1.&T 是一个储存了 T 所在的内存位置的单个值，&str 则是 两个 值：str 的地址和其长度
* 2.Rust 中动态大小类型的常规用法：它们有一些额外的元信息来储存动态信息的大小
* 3.动态大小类型的黄金规则：必须将动态大小类型的值置于某种指针之后
* 4.每一个 trait 都是一个可以通过 trait 名称来引用的动态大小类型
* 5.dyn关键字-dynamic dispatch（动态分发）的首字缩写：dyn用于明确指出我们正在使用trait对象，而不是具体类型
*    它告诉编译器，我们希望在运行时进行方法调用的解析（即动态分发），而不是在编译时
*    通常，dyn会与指针类型（如&、Box、Rc等）一起使用，因为trait对象必须放在某种指针后面。
*
* 本质上，引用类型和指针类型都是动态大小类型，因为它们的大小取决于它们所指向的值。
* 但是因为它们封装了一个动态大小的值，所以虽然编译时候看起来固定大小，其实是动态
* 这也是新类型设计模式，或者说封装的应用。
*
* 三、Sized特质（Sized Trait）
* Sized特质  -- 它告诉编译器某个类型是否固定大小
* 它的实现是自动的，编译器会自动为所有固定大小的类型实现Sized特质
* Rust 隐式的为每一个泛型函数增加了 Sized bound
*
* ?Sized这种结构(?T,T是某个特质)只能用于Sized，告知编译器这个参数可以是固定大小也可以是动态大小
*/

trait Animal {
    fn eat(&self);
}

struct Tiger {
    name: String,
    age: u8,
}
struct Pig {
    name: String,
    age: u8,
}

impl Animal for Tiger {
    fn eat(&self) {
        println!("{}岁{} 正在吃野猪", self.age, self.name);
    }
}

impl Animal for Pig {
    fn eat(&self) {
        println!("{}岁{} 正在吃竹笋和地瓜", self.age, self.name);
    }
}

impl Tiger {
    fn clone(&self) -> Tiger {
        Tiger {
            name: self.name.clone(),
            age: self.age,
        }
    }
}

fn feed_animal_dyn(animals: Vec<Box<dyn Animal>>) {
    for animal in animals {
        animal.eat();
    }
}
/**
 * Sized特质测试，告知编译器这个参数可以是固定大小也可以是动态大小，而非固定大小
 */
fn feed_animal<T: Sized>(animal: T)
where
    T: Animal,
{
    animal.eat();
}

fn main() {

    //这样定义会报告编译错误：doesn't have a size known at compile-time
    //let me:str="lzf"; 
    dst_test();
    dyn_test();
    sized_test();
}

fn sized_test() {
    let tiger = Tiger {
        name: "大王🐯".to_string(),
        age: 3,
    };
    let 武松的老虎 = tiger.clone();
    feed_animal(武松的老虎);
}

/**
 * 动态大小类型测试，使用&T和Box<T>来封装动态大小类型（DST）
 */
fn dst_test() {
    //Box指针封装动态大小类型（DST）.编译器认为Box指针式固定大小，典型的障眼法
    let code: Box<str> = Box::from("Hello, world!");
    println!("{}", code);
    let name:Box<&str> = Box::from("狄仁杰");
    println!("{}", name);

}
/**
 * 动态分发测试，使用dyn告诉编译器这是一个动态分发，而非静态分发，是特质而不是其它的类型（stuct，enum等）
 */
fn dyn_test() {
    let tiger = Tiger {
        name: "松崽🐅".to_string(),
        age: 10,
    };

    let pig = Pig {
        name: "小胖🐖".to_string(),
        age: 5,
    };
    // 必须使用as 关键字，将Tiger和Pig转换为特质对象（trait object）
    // 必须使用dyn关键字，告诉编译器这是一个动态分发（dynamic dispatch），即在运行的时候才使用具体的类型
    let animals = vec![
        Box::new(tiger) as Box<dyn Animal>,
        Box::new(pig) as Box<dyn Animal>,
    ];
    feed_animal_dyn(animals);
}
